package com.atguigu.gmall.order.biz.impl;

import com.atguigu.gmall.cart.entity.CartItem;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.Jsons;
import com.atguigu.gmall.common.utils.UserAuthUtils;
import com.atguigu.gmall.enums.OrderStatus;
import com.atguigu.gmall.enums.ProcessStatus;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.product.SkuDetailFeignClient;
import com.atguigu.gmall.feign.user.UserFeignClient;
import com.atguigu.gmall.feign.ware.WareFeignClient;
import com.atguigu.gmall.order.biz.OrderBizService;
import com.atguigu.gmall.order.entity.OrderDetail;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.order.entity.OrderStatusLog;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.service.OrderStatusLogService;
import com.atguigu.gmall.order.to.OrderMsgTo;
import com.atguigu.gmall.order.vo.DetailVo;
import com.atguigu.gmall.order.vo.OrderConfirmVo;
import com.atguigu.gmall.order.vo.OrderSubmitVo;
import com.atguigu.gmall.user.entity.UserAddress;
import com.atguigu.gmall.util.consts.MqConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author:ahang
 * @create:2022-10-23 1:58
 * @Description:
 */
@Slf4j
@Service
public class OrderBizServiceImpl implements OrderBizService {
    @Autowired
    CartFeignClient cartFeignClient;

    @Autowired
    UserFeignClient userFeignClient;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;

    @Autowired
    OrderInfoService orderInfoService;

    @Autowired
    OrderDetailService orderDetailService;

    @Autowired
    OrderStatusLogService orderStatusLogService;

    @Autowired
    WareFeignClient wareFeignClient;

    @Autowired
    OrderInfoMapper orderInfoMapper;

    @Autowired
    RabbitTemplate rabbitTemplate;


    /**
     * 回去订单确认数据
     * @return
     */
    @Override
    public OrderConfirmVo getOrderConfirmdata() {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();

        //商品列表【购物车中选中的商品列表】
        Result<List<CartItem>> checkedCartItems = cartFeignClient.getCheckedCartItems();
        List<CartItem> cartItems = checkedCartItems.getData();
        List<DetailVo> vos = cartItems.stream().map(item -> {
            DetailVo detailVo = new DetailVo();
            detailVo.setImgUrl(item.getSkuDefaultImg());
            detailVo.setSkuName(item.getSkuName());
            detailVo.setOrderPrice(item.getSkuPrice());
            detailVo.setSkuNum(item.getSkuNum());
            detailVo.setSkuId(item.getSkuId());
            String hasStock = wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum());
            detailVo.setHasStock(hasStock);
            return detailVo;
        }).collect(Collectors.toList());
        orderConfirmVo.setDetailArrayList(vos);

        //商品总数
        Integer integer = cartItems.stream().map(CartItem::getSkuNum)
                .reduce((o1, o2) -> o1 + o2)
                .get();
        orderConfirmVo.setTotalNum(integer);

        //商品总金额
        BigDecimal totalAmount = cartItems.stream().map(item -> item.getSkuPrice()
                .multiply(new BigDecimal(item.getSkuNum().toString())))
                .reduce((o1, o2) -> o1.add(o2)).get();
        orderConfirmVo.setTotalAmount(totalAmount);

        //用户收货地址列表
        Result<List<UserAddress>> userAddress = userFeignClient.getUserAddress();
        orderConfirmVo.setUserAddressList(userAddress.getData());

        //交易号  -- 唯一标识,充当一个订单id
        String token = UUID.randomUUID().toString().replace("-", "");
        //给页面传一个令牌
        orderConfirmVo.setTradeNo(token);
        //给redis中存一个令牌
        redisTemplate.opsForValue().set(RedisConst.ORDER_TOKEN+token,"x",30, TimeUnit.MINUTES);

        return orderConfirmVo;
    }

    /**
     * 提交订单
     * @param orderSubmitVo
     * @param tradeNo
     * @return
     */
    @Override
    public Long submitOrder(OrderSubmitVo orderSubmitVo, String tradeNo) {
        //1.防重校验
        String script = "if redis.call(\"exists\",KEYS[1])\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        Long aLong = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Arrays.asList(RedisConst.ORDER_TOKEN + tradeNo));
        if (aLong == 0) {
            //删除失败； 假令牌 -  真令牌但是被校验过后删除了
            throw new GmallException(ResultCodeEnum.REQ_REPEAT);
        }

        //2.验库存
        List<OrderSubmitVo.OrderDetailListDTO> noStock = orderSubmitVo.getOrderDetailList().stream()
                .filter(item -> {
                    Long skuId = item.getSkuId();
                    Integer skuNum = item.getSkuNum();
                    String hasStock = wareFeignClient.hasStock(skuId, skuNum);
                    return !"1".equals(hasStock);
                }).collect(Collectors.toList());
        if (noStock != null && noStock.size() > 0) {
            throw new GmallException(ResultCodeEnum.SKU_HAS_NO_STOCK);
        }


        //3.验价格
        List<OrderSubmitVo.OrderDetailListDTO> detailListDTOS  = orderSubmitVo.getOrderDetailList().stream()
                .filter(item -> {
                    BigDecimal orderPrice = item.getOrderPrice();
                    //实时价格
                    Result<BigDecimal> skuPrice = skuDetailFeignClient.getPrice(item.getSkuId());
                    return !skuPrice.getData().equals(orderPrice);
                }).collect(Collectors.toList());

        if(detailListDTOS != null && detailListDTOS.size() > 0){
            throw new GmallException(ResultCodeEnum.SKU_PRICE_CHANGE);
        }

        //4.保存订单
        Long orderId = saveOrder(orderSubmitVo, tradeNo);

        //5.删除购物车中的商品
        cartFeignClient.deleteChecked();

        //6.30min未支付就关闭订单
        Long userId = UserAuthUtils.getUserAuthInfo().getUserId();
        OrderMsgTo msgTo = new OrderMsgTo(orderId, userId);

        rabbitTemplate.convertAndSend(MqConst.ORDER_EVENT_EXCHANGE,
                                        MqConst.RK_ORDER_CREATED,
                                        Jsons.tostr(msgTo));
        log.info("订单创建成功：并发送消息给MQ： {}", msgTo);

        return orderId;
    }

    @Override
    public void closeOrder(Long orderId, Long userId) {
        changeOrderStatusByCAS(OrderStatus.UNPAID, ProcessStatus.UNPAID,
                OrderStatus.CLOSED, ProcessStatus.CLOSED, orderId, userId);
    }

    //利用CAS机制实现乐观锁幂等性关单
    /**
     *
     * @param expectOrderStatus  期望的订单状态
     * @param expectProcessStatus 期望的处理状态
     * @param orderStatus   修改成的订单状态
     * @param processStatus 修改成的处理状态
     * @param orderId  订单id
     * @param userId  用户id
     */
    @Override
    public void changeOrderStatusByCAS(OrderStatus expectOrderStatus,
                                       ProcessStatus expectProcessStatus,
                                       OrderStatus orderStatus,
                                       ProcessStatus processStatus,
                                       Long orderId,
                                       Long userId) {
        orderInfoMapper.updateOrderStatusByCAS(expectOrderStatus.name(),
                expectProcessStatus.name(),
                orderStatus.name(),
                processStatus.name(),
                orderId,
                userId);
    }

    /**
     * 保存订单
     * @param orderSubmitVo
     * @param tradeNo
     * @return
     */
    @Override
    public Long saveOrder(OrderSubmitVo orderSubmitVo, String tradeNo) {
        //1、保存订单 order_info
        OrderInfo orderInfo = saveOrderInfo(orderSubmitVo, tradeNo);

        //2、保存订单明细 order_detail
        saveOrderDetail(orderSubmitVo, orderInfo);

        //3、保存此次订单操作日志 order_status_log
        saveOrderStatusLog(orderInfo);

        return orderInfo.getId();

    }

    //保存订单日志
    private void saveOrderStatusLog(OrderInfo orderInfo) {
        OrderStatusLog statusLog = new OrderStatusLog();

        statusLog.setOrderId(orderInfo.getId());
        statusLog.setUserId(orderInfo.getUserId());
        statusLog.setOrderStatus(orderInfo.getOrderStatus());
        statusLog.setOperateTime(new Date());

        orderStatusLogService.save(statusLog);
    }

    //保存订单明细
    private void saveOrderDetail(OrderSubmitVo orderSubmitVo, OrderInfo orderInfo) {
        List<OrderSubmitVo.OrderDetailListDTO> detailList = orderSubmitVo.getOrderDetailList();

        List<OrderDetail> details = detailList.stream().map(item -> {
            OrderDetail orderDetail = new OrderDetail();
            Long id = orderInfo.getId();
            orderDetail.setOrderId(id);
            orderDetail.setUserId(orderInfo.getUserId());
            orderDetail.setSkuId(item.getSkuId());
            orderDetail.setSkuName(item.getSkuName());
            orderDetail.setImgUrl(item.getImgUrl());
            orderDetail.setOrderPrice(item.getOrderPrice());
            orderDetail.setSkuNum(item.getSkuNum());
            orderDetail.setCreateTime(new Date());

            //商品参与的优惠
            orderDetail.setSplitActivityAmount(new BigDecimal("0"));
            orderDetail.setSplitCouponAmount(new BigDecimal("0"));
            //商品的总价
            orderDetail.setSplitCouponAmount(orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum().toString())));
            return orderDetail;
        }).collect(Collectors.toList());

        orderDetailService.saveBatch(details);
    }

    //保存订单详情
    private OrderInfo saveOrderInfo(OrderSubmitVo orderSubmitVo, String tradeNo) {
        OrderInfo orderInfo = new OrderInfo();
        //收货人信息
        orderInfo.setConsignee(orderSubmitVo.getConsignee());
        orderInfo.setConsigneeTel(orderSubmitVo.getConsigneeTel());
        //订单总价
        BigDecimal totalAmount = orderSubmitVo.getOrderDetailList().stream()
                .map(item -> {
                    BigDecimal orderPrice = item.getOrderPrice();
                    Integer skuNum = item.getSkuNum();
                    return orderPrice.multiply(new BigDecimal(skuNum.toString()));
                }).reduce((o1, o2) -> o1.add(o2)).get();
        orderInfo.setTotalAmount(totalAmount);
        //订单状态
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //用户id
        Long userId = UserAuthUtils.getUserAuthInfo().getUserId();
        orderInfo.setUserId(userId);
        //支付方式
        orderInfo.setPaymentWay(orderSubmitVo.getPaymentWay());
        //收货地址
        orderInfo.setDeliveryAddress(orderSubmitVo.getDeliveryAddress());
        //订单备注
        orderInfo.setOrderComment(orderSubmitVo.getOrderComment());
        //对外交易号
        orderInfo.setOutTradeNo(tradeNo);
        //订单体【默认用订单中第一个商品的名字即可】
        String skuName = orderSubmitVo.getOrderDetailList().get(0).getSkuName();
        orderInfo.setTradeBody(skuName);
        //创建订单时间
        orderInfo.setCreateTime(new Date());
        //过期时间【30min 不支付代表订单过期】
        long mills = System.currentTimeMillis() + RedisConst.ORDER_TTL;
        orderInfo.setExpireTime(new Date(mills));
        //订单的处理状态【给后台看】  已支付。 一个 OrderStatus 可能代表很多中情况（处理状态）
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());

//        orderInfo.setTrackingNo();
//        orderInfo.setParentOrderId();

        String imgUrl = orderSubmitVo.getOrderDetailList().get(0).getImgUrl();
        orderInfo.setImgUrl(imgUrl);
        //操作时间
        orderInfo.setOperateTime(new Date());

        orderInfo.setActivityReduceAmount(new BigDecimal("0"));
        orderInfo.setCouponAmount(new BigDecimal("0"));
        //原始金额
        orderInfo.setOriginalTotalAmount(totalAmount);
        //运费
        orderInfo.setFeightFee(new BigDecimal("0"));

//        orderInfo.setRefundableTime();

        orderInfoService.save(orderInfo);
        return orderInfo;
    }
}
